#include "plan_builder.hpp"
#include <stdexcept>

// 构造函数
PlanBuilder::PlanBuilder(const std::unordered_map<std::string, TableSchema> &schema)
    : db_schema(schema) {}

// ========== build 接口 ==========
CompileResult PlanBuilder::build(std::unique_ptr<Stmt> &stmt)
{
    try
    {
        if (auto sel = dynamic_cast<SelectStmt *>(stmt.get()))
            return buildSelect(*sel);
        else if (auto ct = dynamic_cast<CreateTableStmt *>(stmt.get()))
            return buildCreate(*ct);
        else if (auto ins = dynamic_cast<InsertStmt *>(stmt.get()))
            return buildInsert(*ins);
        else if (auto *p = dynamic_cast<CreateIndexStmt *>(stmt.get()))
            return buildCreateIndex(*p);
        else if (auto *p = dynamic_cast<DropTableStmt *>(stmt.get()))
            return buildDrop(*p);
        else if (auto *p = dynamic_cast<DropIndexStmt *>(stmt.get()))
            return buildDrop(*p);
        else if (auto *p = dynamic_cast<DeleteStmt *>(stmt.get()))
            return buildDelete(*p);
        else if (auto *p = dynamic_cast<AlterTableStmt *>(stmt.get()))
            return buildAlter(*p);
        else if (auto *p = dynamic_cast<ShowTablesStmt *>(stmt.get()))
            return buildShowTables(*p);
        else if (auto *p = dynamic_cast<ShowIndicesStmt *>(stmt.get()))
            return buildShowIndices(*p);
        else if (auto *p = dynamic_cast<UpdateStmt *>(stmt.get()))
            return buildUpdate(*p);
        throw std::runtime_error("Unsupported statement type");
    }
    catch (const std::runtime_error &e)
    {
        return makeError("Semantic Error", e.what(), 1, 1);
    }
}

// ========== SELECT ==========
CompileResult PlanBuilder::buildSelect(const SelectStmt &sel)
{
    if (!db_schema.count(sel.table))
        return makeError("Semantic Error",
                         "Table '" + sel.table + "' does not exist.", 1, 1);

    auto plan = std::make_unique<ExecutionPlanNode>();
    plan->op_type = OpType::Project;
    plan->details.project_cols = sel.proj;
    plan->details.table_name = sel.table;

    if (sel.where)
    {
        auto filter = std::make_unique<ExecutionPlanNode>();
        filter->op_type = OpType::Filter;
        filter->details.condition = cloneCondition(sel.where.get());
        filter->details.table_name = sel.table;
        plan->child = std::move(filter);
    }
    else
    {
        auto scan = std::make_unique<ExecutionPlanNode>();
        scan->op_type = OpType::SeqScan;
        scan->details.table_name = sel.table;
        plan->child = std::move(scan);
    } // ORDER BY
    if (!sel.orderBy.empty())
    {
        // auto orderNode = std::make_unique<ExecutionPlanNode>();
        plan->details.order_by = sel.orderBy;
        // orderNode->child = std::move(plan);
        // plan = std::move(orderNode);
    }

    // LIMIT
    if (sel.limit.has_value())
    {
        // auto limitNode = std::make_unique<ExecutionPlanNode>();
        plan->details.limit = sel.limit.value();
        if (sel.offset.has_value())
            plan->details.offset = sel.offset.value();
        // limitNode->child = std::move(plan);
        // plan = std::move(limitNode);
    }

    CompileResult res;
    res.status = "success";
    res.plan = std::move(plan);
    return res;
}

// ========== CREATE TABLE ==========
CompileResult PlanBuilder::buildCreate(const CreateTableStmt &ct)
{
    auto plan = std::make_unique<ExecutionPlanNode>();
    plan->op_type = OpType::CreateTable;
    plan->details.table_name = ct.name;

    for (const ColumnDef &c : ct.cols)
    {
        plan->details.create_cols.push_back(toSchema(c));
    }

    CompileResult res;
    res.status = "success";
    res.plan = std::move(plan);
    return res;
}

// ========== INSERT ==========
CompileResult PlanBuilder::buildInsert(const InsertStmt &ins)
{
    if (!db_schema.count(ins.table))
        return makeError("Semantic Error",
                         "Table '" + ins.table + "' does not exist.", 1, 1);

    auto plan = std::make_unique<ExecutionPlanNode>();
    plan->op_type = OpType::Insert;
    plan->details.table_name = ins.table;

    for (size_t idx = 0; idx < ins.values.size(); ++idx)
    {
        const auto &v = ins.values[idx];
        const auto &colName = ins.colNames[idx];

        Value val; // std::optional<ValueType>

        switch (v.type)
        {
        case InsertStmt::ValType::INT:
            val = ValueType{v.i};
            break;

        case InsertStmt::ValType::DOUBLE:
            val = ValueType{v.d};
            break;

        case InsertStmt::ValType::STRING:
        case InsertStmt::ValType::DATE:
        case InsertStmt::ValType::TIME:
        case InsertStmt::ValType::DATETIME:
        case InsertStmt::ValType::JSON:
            val = ValueType{v.s};
            break;

        case InsertStmt::ValType::BOOL:
            val = ValueType{v.b};
            break;

        case InsertStmt::ValType::NULLTYPE:
            val = std::nullopt; // 直接用 optional 的空值表示 NULL
            break;
        }

        plan->details.insert_values[colName] = val;
    }

    CompileResult res;
    res.status = "success";
    res.plan = std::move(plan);
    return res;
}

// ========== CREATE INDEX ==========
CompileResult PlanBuilder::buildCreateIndex(const CreateIndexStmt &stmt)
{
    if (!db_schema.count(stmt.table))
        return makeError("Semantic Error",
                         "Table '" + stmt.table + "' does not exist.", 1, 1);

    auto plan = std::make_unique<ExecutionPlanNode>();
    plan->op_type = OpType::CreateIndex;
    plan->details.table_name = stmt.table;
    plan->details.index_name = stmt.index_name;
    plan->details.index_columns = stmt.cols;
    plan->details.is_unique = stmt.unique;

    CompileResult res;
    res.status = "success";
    res.plan = std::move(plan);
    return res;
}
// ========== DROP ==========
CompileResult PlanBuilder::buildDrop(const Stmt &stmt)
{
    auto plan = std::make_unique<ExecutionPlanNode>();

    if (auto *p = dynamic_cast<const DropTableStmt *>(&stmt))
    {
        if (!db_schema.count(p->table))
            return makeError("Semantic Error",
                             "Table '" + p->table + "' does not exist.", 1, 1);
        plan->op_type = OpType::DropTable;
        plan->details.table_name = p->table;
    }
    else if (auto *p = dynamic_cast<const DropIndexStmt *>(&stmt))
    {
        bool found = false;
        for (auto &[tname, ts] : db_schema)
        {
            for (auto &idx : ts.indexes)
            {
                if (idx.index_name == p->index_name)
                {
                    found = true;
                    break;
                }
            }
            if (found)
                break;
        }
        if (!found)
            return makeError("Semantic Error",
                             "Index '" + p->index_name + "' does not exist.", 1, 1);
        plan->op_type = OpType::DropIndex;
        plan->details.table_name = p->table;
        plan->details.index_name = p->index_name;
    }
    else
    {
        return makeError("Syntax Error", "Unsupported DROP statement.", 1, 1);
    }

    CompileResult res;
    res.status = "success";
    res.plan = std::move(plan);
    return res;
}

// ========== ALTER ==========
CompileResult PlanBuilder::buildAlter(const AlterTableStmt &stmt)
{
    if (!db_schema.count(stmt.table))
        return makeError("Semantic Error",
                         "Table '" + stmt.table + "' does not exist.", 1, 1);

    auto plan = std::make_unique<ExecutionPlanNode>();
    plan->op_type = OpType::AlterTable;
    plan->details.table_name = stmt.table;

    if (stmt.action == "ADD_COLUMN")
    {
        ColumnSchema col;
        col.name = stmt.colDef.name;
        col.type = colTypeToString(stmt.colDef.t, stmt.colDef.isUnsigned);
        col.length = stmt.colDef.len;
        col.nullable = stmt.colDef.nullable;
        col.unique = stmt.colDef.unique;
        col.primary = stmt.colDef.primary;
        col.auto_increment = stmt.colDef.auto_increment;
        if (stmt.colDef.default_value.has_value())
        {
            col.default_value = stmt.colDef.default_value->asString();
        }
        col.comment = stmt.colDef.comment;
        if (!plan->details.add_cols)
            plan->details.add_cols.emplace();
        plan->details.add_cols->push_back(col);
    }
    else if (stmt.action == "MODIFY_COLUMN")
    {
        if (!plan->details.modified_cols)
            plan->details.modified_cols.emplace();
        (*plan->details.modified_cols)[stmt.colDef.name] = toModifiedColumn(stmt.colDef, "");
    }
    else if (stmt.action == "CHANGE_COLUMN")
    {
        if (!plan->details.modified_cols)
            plan->details.modified_cols.emplace();
        (*plan->details.modified_cols)[stmt.oldName] = toModifiedColumn(stmt.colDef, stmt.newName);
    }
    else if (stmt.action == "DROP_COLUMN")
    {
        if (!plan->details.drop_cols)
            plan->details.drop_cols.emplace();
        plan->details.drop_cols->push_back(stmt.oldName);
    }
    else if (stmt.action == "RENAME_TABLE")
    {
        plan->details.rename_table_name = stmt.newName;
    }
    else if (stmt.action == "MODIFY_COMMENT")
    {
        plan->details.modified_comment = stmt.newComment;
    }
    else
    {
        return makeError("Semantic Error",
                         "Unsupported ALTER action '" + stmt.action + "'", 1, 1);
    }

    CompileResult res;
    res.status = "success";
    res.plan = std::move(plan);
    return res;
}

// ========== DELETE ==========
CompileResult PlanBuilder::buildDelete(const DeleteStmt &del)
{
    if (!db_schema.count(del.table))
        return makeError("Semantic Error",
                         "Table '" + del.table + "' does not exist.", 1, 1);

    auto plan = std::make_unique<ExecutionPlanNode>();
    plan->op_type = OpType::Delete;
    plan->details.table_name = del.table;

    if (del.where)
    {
        plan->details.condition = cloneCondition(del.where.get());
    }

    CompileResult res;
    res.status = "success";
    res.plan = std::move(plan);
    return res;
}
// ========== UPDATE ==========
CompileResult PlanBuilder::buildUpdate(const UpdateStmt &stmt)
{
    if (!db_schema.count(stmt.table))
        return makeError("Semantic Error",
                         "Table '" + stmt.table + "' does not exist.", 1, 1);

    auto plan = std::make_unique<ExecutionPlanNode>();
    plan->op_type = OpType::Update;
    plan->details.table_name = stmt.table;

    plan->details.update_values = stmt.assigns;

    if (stmt.where)
    {
        plan->details.condition = cloneCondition(stmt.where.get());
    }

    CompileResult res;
    res.status = "success";
    res.plan = std::move(plan);
    return res;
}

// ========== SHOW ==========
CompileResult PlanBuilder::buildShowTables(const ShowTablesStmt &)
{
    auto plan = std::make_unique<ExecutionPlanNode>();
    plan->op_type = OpType::ShowTables;

    CompileResult res;
    res.status = "success";
    res.plan = std::move(plan);
    return res;
}
// ========== SHOW INDICES ==========
CompileResult PlanBuilder::buildShowIndices(const ShowIndicesStmt &stmt)
{
    if (!db_schema.count(stmt.table))
        return makeError("Semantic Error",
                         "Table '" + stmt.table + "' does not exist.", 1, 1);

    auto plan = std::make_unique<ExecutionPlanNode>();
    plan->op_type = OpType::ShowIndices;
    plan->details.table_name = stmt.table;

    CompileResult res;
    res.status = "success";
    res.plan = std::move(plan);
    return res;
}
// ========== 错误辅助 ==========
CompileResult PlanBuilder::makeError(const std::string &type,
                                     const std::string &msg,
                                     int line, int col)
{
    CompileResult res;
    res.status = "error";
    res.error.error_type = type;
    res.error.message = msg;
    res.error.line = line;
    res.error.column = col;
    return res;
}

Condition PlanBuilder::cloneCondition(const Condition *cond)
{
    if (!cond)
        return Condition();
    Condition c(cond->type);
    c.column = cond->column;
    c.op = cond->op;
    c.value = cond->value;
    for (const auto &child : cond->children)
    {
        c.children.push_back(std::make_unique<Condition>(
            cloneCondition(child.get())));
    }
    return c;
}
std::string PlanBuilder::colTypeToString(ColType t, bool isUnsigned)
{
    switch (t)
    {
    case ColType::INT:
        if (isUnsigned)
        {
            return "INT UNSIGNED";
        }
        else
        {
            return "INT";
        }
    case ColType::TINYINT:
        if (isUnsigned)
        {
            return "TINYINT UNSIGNED";
        }
        else
        {
            return "TINYINT";
        }
    case ColType::SMALLINT:
        if (isUnsigned)
        {
            return "SMALLINT UNSIGNED";
        }
        else
        {
            return "SMALLINT";
        }
    case ColType::BIGINT:
        if (isUnsigned)
        {
            return "BIGINT UNSIGNED";
        }
        else
        {
            return "BIGINT";
        }
    case ColType::FLOAT:
        return "FLOAT";
    case ColType::DOUBLE:
        return "DOUBLE";
    case ColType::CHAR:
        return "CHAR";
    case ColType::VARCHAR:
        return "VARCHAR";
    case ColType::TEXT:
        return "TEXT";
    case ColType::BLOB:
        return "BLOB";
    case ColType::DATE:
        return "DATE";
    case ColType::TIME:
        return "TIME";
    case ColType::DATETIME:
        return "DATETIME";
    case ColType::TIMESTAMP:
        return "TIMESTAMP";
    case ColType::BOOLEAN:
        return "BOOLEAN";
    case ColType::JSON:
        return "JSON";
    default:
        return "UNKNOWN";
    }
}
// ========= 公共转换函数 =========
ColumnSchema PlanBuilder::toSchema(const ColumnDef &c)
{
    ColumnSchema col;
    col.name = c.name;
    col.type = colTypeToString(c.t, c.isUnsigned); // 你之前写的 ColType → string 转换
    col.length = c.len;

    col.nullable = c.nullable;
    col.unique = c.unique;
    col.primary = c.primary;
    col.auto_increment = c.auto_increment;

    if (c.default_value)
        col.default_value = c.default_value->asString();
    col.comment = c.comment;

    return col;
}
ModifiedColumn PlanBuilder::toModifiedColumn(const ColumnDef &c, const std::string &newName)
{
    ModifiedColumn mc;
    mc.name = newName.empty() ? c.name : newName; // CHANGE 时可传新名字
    mc.type = colTypeToString(c.t, c.isUnsigned);
    mc.length = c.len;

    mc.nullable = c.nullable;
    mc.unique = c.unique;
    mc.primary = c.primary;
    mc.auto_increment = c.auto_increment;

    if (c.default_value)
        mc.default_value = c.default_value->asString();
    if (!c.comment.empty())
        mc.comment = c.comment;

    return mc;
}
